<?php

namespace app\service;

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use think\facade\Lang;

class WechatPayService
{
    private $config;
    private $instance;

    public function __construct()
    {
        $this->config = config('app.wechat_pay');
        $this->initializeSDK();
    }

    /**
     * 初始化微信支付SDK.
     */
    private function initializeSDK()
    {
        try {
            // 商户相关配置
            $merchantId = $this->config['mch_id'];
            $merchantApiKey = $this->config['mch_secret']; // v3 API密钥
            $merchantSerialNumber = $this->config['serial_no']; // 商户证书序列号
            $privateKeyPath = app()->getRootPath().'cert/'.$this->config['private_key_path'] ?? ''; // 商户私钥路径
            $platformCertPath = app()->getRootPath().'cert/'.$this->config['public_key'] ?? ''; // 微信支付平台证书路径

            // 判断私钥文件是否存在
            if (!file_exists($privateKeyPath)) {
                throw new \Exception(Lang::get('merchant_private_key_not_exist').': '.$privateKeyPath);
            }

            // 判断微信支付平台证书是否存在
            if (!file_exists($platformCertPath)) {
                throw new \Exception(Lang::get('wechat_platform_cert_not_exist').': '.$platformCertPath);
            }

            // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
            $merchantPrivateKeyFilePath = 'file://'.$privateKeyPath;
            $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

            // 从本地文件中加载「微信支付平台证书」(可使用证书下载工具得到），用来验证微信支付应答的签名
            $platformCertificateFilePath = 'file://'.$platformCertPath;
            $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

            // 从「微信支付平台证书」中获取「证书序列号」
            $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

            // 构建配置数组
            $builderConfig = [
                'mchid' => $merchantId,
                'serial' => $merchantApiKey,  // 商户API证书序列号
                'privateKey' => $merchantPrivateKeyInstance, // 商户API私钥
                'certs' => [
                    $platformCertificateSerial => $platformPublicKeyInstance,
                ],
            ];

            // 初始化支付API实例
            $this->instance = Builder::factory($builderConfig);
            // \think\facade\Log::info('微信支付SDK初始化成功，配置：' . json_encode([
            //     'mchid' => $merchantId,
            //     'serial' => $merchantSerialNumber,
            //     'platformCertSerial' => $platformCertificateSerial
            // ]));
        } catch (\Exception $e) {
            // 记录错误日志
            \think\facade\Log::error(Lang::get('wechat_pay_sdk_init_failed').': '.$e->getMessage());
            throw $e; // 继续抛出异常，以便调用者知道出错
        }
    }

    /**
     * 从证书文件中提取序列号.
     *
     * @param string $certPath 证书路径
     *
     * @return string|null 证书序列号
     */
    private function getCertificateSerialNumber($certPath)
    {
        try {
            $cert = file_get_contents($certPath);
            $certData = openssl_x509_parse($cert);
            if (isset($certData['serialNumber'])) {
                return $certData['serialNumber'];
            }

            return null;
        } catch (\Exception $e) {
            \think\facade\Log::error(Lang::get('cert_serial_extract_failed').': '.$e->getMessage());

            return null;
        }
    }

    /**
     * 生成Native支付二维码URL.
     *
     * @param string $outTradeNo 商户订单号
     * @param float  $totalFee   支付金额（元）
     * @param string $body       商品描述
     * @param string $attach     附加数据
     *
     * @return array 支付结果
     */
    public function createNativePayment($outTradeNo, $totalFee, $body, $attach = '')
    {
        try {
            // 将元转换为分
            $totalFee = intval($totalFee * 100);
            $ary = [
                'appid' => $this->config['mini_appid'],
                'mchid' => $this->config['mch_id'],
                'description' => $body,
                'out_trade_no' => $outTradeNo,
                'notify_url' => env('app.domain', 'https://docs.spacetimesc.com/').'v6/combo/resolveNotify',
                'amount' => [
                    'total' => $totalFee,
                    'currency' => 'CNY',
                ],
                'attach' => $attach,
            ];
            \think\facade\Log::info('微信支付SDK初始化成功，配置：'.json_encode($ary));
            // Native支付API
            $resp = $this->instance
                ->chain('v3/pay/transactions/native')
                ->post(['json' => $ary]);

            $statusCode = $resp->getStatusCode();
            if ($statusCode == 200) {
                // API返回的是完整的JSON，需要解析
                $result = json_decode($resp->getBody(), true);

                if (!empty($result['code_url'])) {
                    return [
                        'code' => 0,
                        'msg' => '支付创建成功',
                        'data' => [
                            'code_url' => $result['code_url'],
                            'out_trade_no' => $outTradeNo,
                            'total_fee' => $totalFee / 100,
                        ],
                    ];
                }
            }

            return [
                'code' => 1,
                'msg' => '支付创建失败: HTTP状态 '.$statusCode,
                'data' => [],
            ];
        } catch (\Exception $e) {
            return [
                'code' => 1,
                'msg' => '支付接口异常: '.$e->getMessage(),
                'data' => [],
            ];
        }
    }

    /**
     * 查询订单状态
     *
     * @param string $outTradeNo 商户订单号
     *
     * @return array 订单状态
     */
    public function queryOrder($outTradeNo)
    {
        $params = [
            'appid' => $this->appId,
            'mch_id' => $this->mchId,
            'out_trade_no' => $outTradeNo,
            'nonce_str' => $this->generateNonceStr(),
        ];

        // 签名
        $params['sign'] = $this->generateSign($params);

        // 转换为XML
        $xml = $this->arrayToXml($params);

        // 发送请求
        $result = $this->postXmlCurl($xml, 'https://api.mch.weixin.qq.com/pay/orderquery');

        // 解析返回结果
        $resultArray = $this->xmlToArray($result);

        if ($resultArray['return_code'] === 'SUCCESS' && $resultArray['result_code'] === 'SUCCESS') {
            $tradeState = $resultArray['trade_state'];

            return [
                'code' => 0,
                'msg' => '查询成功',
                'data' => [
                    'out_trade_no' => $resultArray['out_trade_no'],
                    'trade_state' => $tradeState,
                    'trade_state_desc' => $resultArray['trade_state_desc'],
                    'paid' => $tradeState === 'SUCCESS',
                ],
            ];
        } else {
            return [
                'code' => 1,
                'msg' => $resultArray['err_code_des'] ?? $resultArray['return_msg'] ?? '查询失败',
                'data' => [],
            ];
        }
    }

    /**
     * 生成随机字符串.
     *
     * @return string
     */
    private function generateNonceStr()
    {
        return md5(uniqid(mt_rand(), true));
    }

    /**
     * 生成签名.
     *
     * @param array $params 参数
     *
     * @return string
     */
    private function generateSign($params)
    {
        ksort($params);

        $stringA = '';
        foreach ($params as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $stringA .= $k.'='.$v.'&';
            }
        }

        $stringSignTemp = $stringA.'key='.$this->key;

        return strtoupper(md5($stringSignTemp));
    }

    /**
     * 数组转XML.
     *
     * @param array $arr 数组
     *
     * @return string
     */
    private function arrayToXml($arr)
    {
        $xml = '<xml>';
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<'.$key.'>'.$val.'</'.$key.'>';
            } else {
                $xml .= '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
            }
        }
        $xml .= '</xml>';

        return $xml;
    }

    /**
     * XML转数组.
     *
     * @param string $xml XML字符串
     *
     * @return array
     */
    private function xmlToArray($xml)
    {
        libxml_disable_entity_loader(true);

        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 发送XML请求
     *
     * @param string $xml    XML数据
     * @param string $url    请求地址
     * @param int    $second 超时时间
     *
     * @return string
     */
    private function postXmlCurl($xml, $url, $second = 30)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        curl_close($ch);

        return $data;
    }

    /**
     * 生成H5支付链接（用于微信内浏览器或移动端浏览器）.
     *
     * @param string $outTradeNo 商户订单号
     * @param float  $totalFee   支付金额（元）
     * @param string $body       商品描述
     * @param string $attach     附加数据
     *
     * @return array 支付结果，包含H5支付链接
     */
    public function createH5Payment($outTradeNo, $totalFee, $body, $attach = '')
    {
        try {
            // 将元转换为分
            $totalFee = intval($totalFee * 100);
            $ary = [
                'appid' => $this->config['app_id'],  // 使用公众号APPID
                'mchid' => $this->config['mch_id'],
                'description' => $body,
                'out_trade_no' => $outTradeNo,
                'notify_url' => env('app.domain', 'https://docs.spacetimesc.com/').'v6/combo/resolveNotify',
                'amount' => [
                    'total' => $totalFee,
                    'currency' => 'CNY',
                ],
                'attach' => $attach,
                'scene_info' => [
                    'payer_client_ip' => request()->ip(),
                    'h5_info' => [
                        'type' => 'Wap',
                    ],
                ],
            ];

            // H5支付API
            \think\facade\Log::info('微信H5支付请求参数：'.json_encode($ary));
            $resp = $this->instance
                ->chain('v3/pay/transactions/h5')
                ->post(['json' => $ary]);

            $statusCode = $resp->getStatusCode();
            if ($statusCode == 200) {
                $result = json_decode($resp->getBody(), true);

                if (!empty($result['h5_url'])) {
                    return [
                        'code' => 0,
                        'msg' => '支付创建成功',
                        'data' => [
                            'h5_url' => $result['h5_url'],
                            'out_trade_no' => $outTradeNo,
                            'total_fee' => $totalFee / 100,
                        ],
                    ];
                }
            }

            return [
                'code' => 1,
                'msg' => '支付创建失败: HTTP状态 '.$statusCode,
                'data' => [],
            ];
        } catch (\Exception $e) {
            \think\facade\Log::error('微信H5支付异常：'.$e->getMessage());

            return [
                'code' => 1,
                'msg' => '支付接口异常: '.$e->getMessage(),
                'data' => [],
            ];
        }
    }
}
